<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>合金弹头觉醒-机芯装配</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14"></script>
    <script src="https://unpkg.com/http-vue-loader"></script> 
    <!-- 引入样式 -->
    <link
      rel="stylesheet"
      href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"
    />
    <!-- 引入组件库 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <style>
      :root {
        --primary: #409eff;
      }
      html,
      body {
        padding: 0;
        height: 100%;
        margin: 0;
      }
      div,
      p {
        box-sizing: border-box;
      }
      p {
        padding: 0;
        margin: 0;
      }
      h1,
      h2,
      h3,
      h4 {
        margin: 10px 0;
        padding: 0;
      }
      #app {
        height: 100%;
        background: #fbfbfb;
      }
      .inner {
        min-width: 1200px;
        height: 100%;
        padding: 40px 100px 0 100px;
        display: flex;
        flex-direction: column;
      }
      .el-tabs {
        flex: 1;
        height: 0;
        display: flex;
        flex-direction: column;
      }
      .el-tabs .el-tabs__content {
        height: 0;
        flex: 1;
        padding-bottom: 20px;
      }
      .el-tab-pane {
        height: 100%;
      }
      .el-form-item--small .el-form-item__label {
        line-height: 1;
        background: #f5f7fa;
        width: 100%;
        padding: 4px;
        text-align: center;
        border-top: 1px solid #ebeef5;
        border-left: 1px solid #ebeef5;
        border-right: 1px solid #ebeef5;
        border-radius: 4px 4px 0 0;
      }

      .add-tab,
      .movement-tab,
      .main-tab {
        height: 100%;
        display: flex;
        flex-direction: column;
      }
      .el-table--small .el-table__cell {
        padding: 0;
        height: 40px;
      }
      .movement-list {
        height: 0;
        margin-top: 20px;
        flex: 1;
        overflow: auto;
      }
      .movement-list .el-card {
        display: inline-block;
        vertical-align: top;
        margin-right: 10px;
        margin-bottom: 10px;
        font-size: 14px;
        color: #5b5c5e;
      }
      .movement-card p {
        display: flex;
        justify-content: space-between;
      }
      .movement-card .el-card__body {
        padding: 0 10px 10px 10px;
        min-width: 154px;
        min-height: 190px;
      }
      .movement-list .card-edit {
        background: var(--primary);
        color: #fff;
      }
      .table-group {
        height: 0;
        flex: 1;
        overflow: auto;
      }
      .stars {
        transform: scale(0.7);
      }
    </style>
  </head>
  <body>
    <div id="app">
      <div class="inner">
        <el-tabs v-model="activeTab">
          <el-tab-pane v-for="tab in tabs" :key="tab.name" v-bind="tab">
            <component :is="tab.componentName"></component>
          </el-tab-pane>
        </el-tabs>
      </div>
    </div>
  </body>
  <!-- 首页 -->
  <script type="text/template" id="main">
    <div class="main-tab">
      <!-- 搜索栏 -->
      <el-form inline label-position="top" :show-message="false">
        <el-form-item v-for="prop in app.dataset.properties" :label="prop" :key="prop">
          <span slot="label">
            {{prop}}
            <i class="el-icon-delete" @click="handleDelete(query,[prop])"></i>
          </span>
          <el-input-number v-model="query[prop]" controls-position="right" :step="5" :min="0" :max="200" style="width: 115px"></el-input-number>
        </el-form-item>

        <!-- 机芯/数量 -->
        <el-form-item label="机芯/数量" v-for="(item,index) in otherQuery.group" :key="index">
          <span slot="label">
            机芯/数量
            <i class="el-icon-delete" @click="handleDelete(item,['name','value'])"></i>
          </span>
          <el-select v-model="item.name" style="width: 120px" clearable filterable placeholder="">
            <el-option v-for="option in handleNames(index)" :key="option" :label="option" :value="option"></el-option>
          </el-select>
          <el-input-number v-model="item.value" controls-position="right" step-strictly :min="1" :max="6" style="width: 74px"></el-input-number>
        </el-form-item>

        <el-form-item label="搜索/重置">
          <el-button type="primary" icon="el-icon-search" @click="onSearch" :loading="loading">
            <span>{{res.length > max ? max + '+' : res.length || ''}}</span>
            <span v-if="time">{{time}}s</span>
            <el-tooltip v-if="res.length > max" placement="top" content="匹配的搜索结果超过了上限。一些更高属性的组合可能沒有被显示出來，若需要查看更多结果，请上调属性值">
              <i  class="el-icon-question"></i>
            </el-tooltip>
          </el-button>
          <el-button type="primary" icon="el-icon-refresh" @click="onReset"></el-button>
          <!-- <el-button type="primary" icon="el-icon-plus"></el-button> -->
        </el-form-item>
      </el-form>
      <!-- 搭配结果 -->
      <div class="table-group" v-infinite-scroll="loadMore">
        <el-empty v-if="!data.length"></el-empty>
        <div v-for="(group,index) in data" :key="index">
          <p style="padding: 20px 0; text-align:center">
            <span>{{index + 1}}、</span>
            <span v-if=" !(index % size)">
              <span>第{{Math.ceil((index+1)/size)}}页</span>/<span>共{{Math.ceil(res.length/size)}}页</span>
              <span>/ {{data.length}}</span>
            </span>
          </p>
          <el-table :data="group" border :header-cell-style="(data) => cellStyle(data,true)" :cell-style="cellStyle" show-summary :default-sort = "{prop: 'name', order: 'descending'}">
            <el-table-column type="index" label="序号" width="60" align="center"></el-table-column>
            <el-table-column v-for="column in columns" v-bind="column" align="center">
              <template  slot-scope="{row}">
                <div>
                  <span>{{row[column.prop]}}</span>
                  <el-tag v-if="column.prop==='name' && row.season" effect="dark" size="mini" type="primary">{{row.season}}</el-tag>
                  <p v-if="column.prop === 'name'" class="stars">{{'⧫'.repeat(row.star || 0)}}</p>
                  <p v-if="column.prop === 'direction'">{{row.mainProp}}</p>
                </div>
              </template>
            </el-table-column>
          </el-table>
        </div>
      </div>
    </div>
  </script>

  <!-- 机芯列表 -->
  <script type="text/template" id="movement">
    <div class="movement-tab">
      <!-- 搜索栏 -->
      <el-form inline label-position="top" :model="query">
        <el-form-item label="名称" prop="name">
          <el-select v-model="query.name" style="width: 120px" clearable filterable placeholder="">
            <el-option v-for="option in names" :key="option" :label="option" :value="option"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="位置" prop="direction">
          <el-select v-model="query.direction" style="width: 120px" clearable filterable placeholder="">
            <el-option v-for="option in directions" :key="option" :label="option" :value="option"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="属性" prop="prop">
          <el-select v-model="query.prop" style="width: 120px" clearable filterable placeholder="">
            <el-option v-for="option in properties" :key="option" :label="option" :value="option"></el-option>
          </el-select>
          <el-input-number v-model="query.value" :controls="false" :min="0" :max="200" style="width: 80px"></el-input-number>
        </el-form-item>
        <el-form-item label="搜索/重置">
          <el-button type="primary" icon="el-icon-search" @click="onSearch" :loading="loading"></el-button>
          <el-button type="primary" icon="el-icon-refresh" @click="onReset"></el-button>
        </el-form-item>
      </el-form>
      <!-- 结果 -->
      <div style="height:0;flex:1">
        <el-table :data="data" border height="100%" :header-cell-style="headerStyle">
          <el-empty slot="empty"></el-empty>
          <el-table-column type="index" width="80" label="序号" align="center" fixed="left"></el-table-column>
          <el-table-column v-for="column in columns" v-bind="column" :min-width="100" align="center" sortable>
            <template  slot-scope="{row}">
              <div>
                <span>{{row[column.prop]}}</span>
                <el-tag v-if="column.prop==='name' && row.season" effect="dark" size="mini" type="primary">{{row.season}}</el-tag>
                <p v-if="column.prop === 'name'" class="stars">{{'⧫'.repeat(row.star || 0)}}</p>
                <p v-if="column.prop === 'direction'">{{row.mainProp}}</p>
              </div>
            </template>
          </el-table-column>
        </el-table>
      </div>
    </div>
  </script>
  <!-- 机芯管理 -->
  <script type="text/template" id="add">
    <div class="add-tab">
      <el-form ref="form" :model="formData" inline label-position="top" :rules="rules">
        <el-form-item :label="item.label" :prop="item.prop" v-for="item in formItems" :ke="item.prop">
          <span slot="label">
            {{item.label}}
            <i class="el-icon-search"></i>
          </span>
          <el-select v-model="formData[item.prop]" style="width: 120px" clearable filterable placeholder="">
            <el-option v-for="option in app.dataset[item.options]" :key="option" :label="option" :value="option"></el-option>
          </el-select>
        </el-form-item>
          <!-- 机芯基本属性 -->
          <el-form-item label="item.prop" :prop="item.prop" v-for="(item,index) in myProperties" :key="item.prop">
            <span slot="label">
              <i class="el-icon-delete" @click="removeProp(index)"></i>
            </span>
            <el-select :value="item.prop" style="width: 120px" clearable @change="onPropChange($event, item)" filterable placeholder="">
              <el-option v-for="option in properties(index)" :key="option" :label="option" :value="option"></el-option>
            </el-select>
            <el-input-number v-if="item.prop" v-model="formData[item.prop]" :controls="false" :min="0" :max="200" style="width: 74px"></el-input-number>
          </el-form-item>
      </el-form>
      <div>
        <el-button type="primary" icon="el-icon-plus" @click="addProp">属性</el-button>
        <el-button type="primary" @click="onSave" icon="el-icon-plus">保存/{{formData.id ? '编辑' : '添加'}}</el-button>
        <el-button type="primary" @click="onReset" icon='el-icon-refresh'>清空{{formData.id?'/撤消编辑' : ''}}</el-button>
        <el-button type="primary" @click="removeAll" icon="el-icon-delete">删除所有机芯</el-button>
        <el-button type="primary" @click="onSearch" icon="el-icon-search">查询  {{data.length}}</el-button>
      </div>

      <div class="movement-list">
        <el-empty v-if="!data.length"></el-empty>
        <el-card class="movement-card" :class="{'card-edit': formData.id === item.id}" v-for="(item,index) in data" :key="item.id">
          <p>
            <h4>
              {{item.name}}
              <el-tag v-if="item.season" effect="dark" size="mini" type="primary">{{item.season}}</el-tag>
              <span  style="float:right; cursor: pointer" v-if="formData.id !== item.id">
                <i class="el-icon-edit" @click="initEdit(item)"></i>
                <i  class="el-icon-delete" @click="remove(item)"></i>
              </span>
            </h4>
          </p>

          <p><span>{{'⧫'.repeat(item.star || 0)}}</span><span>{{item.direction}}</span></p>
          <p>
            <h4>{{item.mainProp}}</h4>
          </p>
          <template v-for="prop in app.dataset.properties">
            <p :class="{light: !item[prop]}" v-if="item[prop]">
              <span>{{prop}}：</span>
              <span>{{item[prop] || 0}}</span>
            </p>
          </template>
        </el-card>
      </div>
    </div>
  </script>
  <script>
    Vue.prototype.$ELEMENT = {
      size: "small",
    };
    const storage = {
      get(key) {
        if (!key) return;
        let data = this.data();
        return data[key];
      },
      set(key, value = "") {
        if (!key) return;
        let data = this.data();
        data[key] = value;
        localStorage.setItem("MetalSlug", JSON.stringify(data));
      },
      remove(key) {
        if (!key) return;
        let data = this.data();
        delete data[key];
        localStorage.setItem("MetalSlug", JSON.stringify(data));
      },
      clear() {
        localStorage.removeItem("MetalSlug");
      },
      data() {
        let data = localStorage.getItem("MetalSlug");
        return data ? JSON.parse(data) : {};
      },
      updateMovement(data) {
        this.set("movements", data);
      },
    };

    //首页
    Vue.component("MainTab", {
      template: document.getElementById("main"),
      inject: ["app"],
      props: ["message"],
      data() {
        return {
          loading: false,
          query: {},
          otherQuery: {
            group: [{}, {}, {}],
          },
          columns: [
            { label: "名称", prop: "name" },
            { label: "位置", prop: "direction" },
          ],
          headerStyle: { background: "#F5F7FA", color: "#555" },
          data: [],
          res: [],
          max: 500,
          page: 1,
          size: 10,
          time: 0,
        };
      },
      computed: {
        isGroup() {
          return Object.keys(this.need).length > 0;
        },
        need() {
          return this.otherQuery.group.reduce((need, item) => {
            if (item.name && item.value) {
              need[item.name] = item.value;
            }
            return need;
          }, {});
        },
        queryProps() {
          return Object.keys(this.query).filter((key) => this.query[key]);
        },
      },
      created() {
        this.columns = [
          ...this.columns,
          ...this.app.dataset.properties.map((v) => {
            return {
              label: v,
              prop: v,
            };
          }),
        ];
      },
      mounted() {},
      methods: {
        cellStyle({column}, isHeader = false) {
          if(this.query[column.property]) {
            return {
              background: '#F5F7FA',
              color: 'var(--primary)',
            }
          }
          return isHeader ? this.headerStyle : {};
        },
        handleDelete(data, keys) {
          keys.forEach((key) => {
            this.$delete(data, key);
          });
        },
        handleNames(index) {
          let hasName = this.otherQuery.group
            .filter((v, i) => i !== index && v.name)
            .map((v) => v.name);
          return this.app.dataset.names.filter(
            (name) => !hasName.includes(name)
          );
        },
        loadMore() {
          if (this.page * this.size >= this.res.length) return;
          this.data.push(
            ...this.res.slice(this.page++ * this.size, this.page * this.size)
          );
        },
        sum(list) {
          return list.reduce((sum, item) => {
            item.sum = +this.queryProps
              .reduce((res, prop) => {
                res += item[prop] || 0;
                return res;
              }, 0)
              .toFixed(1);
            sum += item.sum;
            return sum;
          }, 0);
        },
        onSearch() {
          if (!this.queryProps.length) {
            return this.$message.warning("至少查询一个属性搭配");
          }
          let startTime = Date.now();
          this.loading = true;
          this.page = 1;
          this.data = [];
          this.res = [];

          let res = [];
          let track = [];
          let used = {
            左: 0,
            上: 0,
            右: 0,
          };
          let length = this.isGroup
            ? this.otherQuery.group.reduce((total, item) => {
                total += item.value || 0;
                return total;
              }, 0)
            : 6;
          this.backtrack(
            this.app.dataset.source,
            track,
            used,
            res,
            0,
            Math.min(6, length)
          );
          this.time = ((Date.now() - startTime) / 1000).toFixed(3);
          res.sort((a, b) => this.sum(b) - this.sum(a));
          if (!res.length) this.$message.warning("未查询到装配组合");
          this.res = res;
          this.data = res.slice(0, this.size);
          this.loading = false;
        },
        backtrack(list, track, used, res, start, length) {
          if (this.valid(track)) {
            res.push([...track]);
          }
          if (track.length === length) return;

          for (let i = start; i < list.length; i++) {
            if (res.length > this.max) return; // 组合太多了
            if (used[list[i].direction] === 2) continue; // 左 上 右 最多各 2个
            if (this.isGroup && !this.need[list[i].name]) continue; // 指定机芯
            track.push(list[i]);
            used[list[i].direction]++;
            this.backtrack(list, track, used, res, i + 1, length);
            used[list[i].direction]--;
            track.pop();
          }
        },
        valid(list) {
          if (!list.length) return false;
          let count = {};
          let res = list.reduce((res, item) => {
            this.queryProps.forEach((prop) => {
              res[prop] = (res[prop] || 0) + (item[prop] || 0);
            });
            count[item.name] = (count[item.name] || 0) + 1;
            return res;
          }, {});
          if (
            this.isGroup &&
            Object.keys(count).some((name) => count[name] > this.need[name])
          ) {
            // 不能超过指定装配机芯数量
            return false;
          }
          return this.queryProps.every((prop) => res[prop] >= this.query[prop]);
        },
        onReset() {
          this.query = {};
          this.otherQuery = {
            group: [{}, {}, {}],
          };
          this.data = [];
          this.res = [];
          this.page = 1;
        },
      },
    });

    //机芯
    Vue.component("MovementTab", {
      template: document.getElementById("movement"),
      inject: ["app"],
      data() {
        return {
          loading: false,
          query: {},
          formItems: [],
          columns: [
            { label: "名称", prop: "name", fixed: "left" },
            { label: "位置", prop: "direction", fixed: "left" },
          ],
          headerStyle: { background: "#F5F7FA", color: "#555" },
          data: [],
        };
      },
      computed: {
        names() {
          return this.app.dataset.names;
        },
        directions() {
          return this.app.dataset.directions;
        },
        properties() {
          return this.app.dataset.properties;
        },
      },
      mounted() {
        this.columns = [
          ...this.columns,
          ...this.app.dataset.properties.map((v) => {
            return {
              label: v,
              prop: v,
            };
          }),
        ];
        this.onSearch();
      },
      actived() {
        this.onSearch();
      },
      methods: {
        onSearch() {
          const { name, direction, prop, value } = this.query;
          this.data = this.app.dataset.source.filter((v) => {
            const filterName = !name || v.name === name;
            const filterDirection = !direction || v.direction === direction;
            const filterValue = this.compare(v, prop, value);
            return filterName && filterDirection && filterValue;
          });
        },
        compare(data, prop, value) {
          if (!prop || !value) return true;
          let propValue = data[prop];
          if (!propValue) return false;
          return propValue >= value;
        },
        onReset() {
          this.query = {};
          this.onSearch();
        },
      },
    });

    // 机芯管理
    Vue.component("AddTab", {
      template: document.getElementById("add"),
      inject: ["app"],
      data() {
        return {
          formData: {},
          rules: {
            name: { required: true, message: "请选择机芯", trigger: "change" },
            star: { required: true, message: "请选择星级", trigger: "change" },
            mainProp: {
              required: true,
              message: "请选择主属性",
              trigger: "change",
            },
            direction: {
              required: true,
              message: "请选择机芯位置",
              trigger: "change",
            },
          },
          formItems: [
            { label: "名称", prop: "name", options: "names" },
            { label: "星级", prop: "star", options: "stars" },
            { label: "位置", prop: "direction", options: "directions" },
            { label: "主属性", prop: "mainProp", options: "mainProps" },
            { label: "赛季", prop: "season", options: "seasons" },
          ],
          myProperties: [],
          data: [],
        };
      },
      mounted() {
        this.app.dataset.properties.forEach((prop) => {
          this.rules[prop] = {
            required: true,
            message: "请选择机芯属性与值",
            trigger: "blur",
          };
        });
        this.onSearch();
      },
      methods: {
        onSearch() {
          const { id, name, star, direction, season, mainProp } = this.formData;
          this.data = this.app.dataset.source.filter((v) => {
            if (id) return v.id === id;
            const fName = !name || v.name === name;
            const fStar = !star || v.star === star;
            const fDirection = !direction || v.direction === direction;
            const fSeason = !season || v.season === season;
            const fMainProp = !mainProp || v.mainProp === mainProp;
            return fName && fStar && fDirection && fSeason && fMainProp;
          });
        },
        initEdit(data) {
          this.myProperties = this.app.dataset.properties
            .filter((p) => data[p])
            .map((v) => {
              return {
                prop: v,
              };
            });
          this.formData = { ...data };
        },
        addProp() {
          if (this.myProperties.length >= 4) return;
          this.myProperties.push({ prop: "" });
        },
        removeProp(index) {
          const prop = this.myProperties[index].prop;
          this.$delete(this.formData, prop);
          this.myProperties.splice(index, 1);
        },
        properties(index) {
          let has = this.myProperties
            .filter((v, i) => i !== index)
            .map((v) => v.prop);
          return this.app.dataset.properties.filter((v) => !has.includes(v));
        },
        onPropChange(key, item) {
          if (item.prop) {
            this.$delete(this.formData, item.prop);
          }
          item.prop = key;
        },
        onSave() {
          this.$refs.form
            .validate()
            .then(() => {
              let list = [...this.app.dataset.source];
              if (!this.formData.id) {
                let props = Object.keys(this.formData);
                if (
                  list.some((v) =>
                    props.every((prop) => this.formData[prop] === v[prop])
                  )
                ) {
                  return this.$message.warning("不能重复添加机芯");
                }
                list.push({ id: Date.now(), ...this.formData });
              } else {
                let index = list.findIndex((v) => v.id === this.formData.id);
                list[index] = { ...this.formData };
                this.onReset();
              }
              this.$message.success("保存成功");
              storage.updateMovement(list);
              this.app.update();
              this.onSearch();
            })
            .catch(() => {});
        },
        remove(item) {
          this.$confirm("是否删除此机芯?", "提示", {
            confirmButtonText: "确定",
            cancelButtonText: "取消",
            type: "warning",
          })
            .then(() => {
              let list = this.app.dataset.source.filter(
                (v) => v.id !== item.id
              );
              storage.updateMovement(list);
              this.app.update();
              this.onSearch();
            })
            .catch(() => {});
        },
        removeAll() {
          this.$confirm("此操作将删除全部机芯，是否删除?", "提示", {
            confirmButtonText: "确定",
            cancelButtonText: "取消",
            type: "warning",
          })
            .then(() => {
              storage.updateMovement([]);
              this.app.update();
              this.onSearch();
            })
            .catch(() => {});
        },
        onReset() {
          this.formData = {};
          this.myProperties = [];
          this.$nextTick(() => this.$refs.form.clearValidate());
          this.onSearch();
        },
      },
    });
    //app
    new Vue({
      el: "#app",
      provide() {
        return {
          app: this,
        };
      },
      data: {
        activeTab: "main",
        tabs: [
          { label: "装配", name: "main", componentName: "MainTab" },
          { label: "我的机芯", name: "movement", componentName: "MovementTab" },
          { label: "机芯管理", name: "add", componentName: "AddTab" },
        ],
        dataset: {
          source: [],
          stars: [1, 2, 3, 4],
          seasons: ["s1"],
          names: [
            "海德",
            "大约翰",
            "战争图腾",
            "毁灭钢虫",
            "龙之助",
            "龙之助二型",
            "海撒旦",
            "大杰狂弹",
            "联合一号",
            "联合二号",
            "重坦舒尔",
            "恐爪战甲",
            "惊啸机甲",
            "格洛纳",
            "雷式空母",
            "六臂神",
            "水渠潜艇",
            "沙暴潜艇",
            "合金支援",
            "太空坦克",
          ],
          mainProps: ["功击力", "生命值", "防御力"],
          directions: ["左", "上", "右"],
          properties: [
            "暴击率",
            "无视防御",
            "暴击伤害",
            "近程增伤",
            "远程增伤",
            "异常增伤",
            "命中率",
            "伤害减免",
            "格档率",
            "暴击抵抗",
            "闪避率",
            "无视防御抵抗",
            "暴伤抵抗",
            "格档抵抗",
          ],
        },
      },
      created() {
        this.update();
      },
      mounted() {},
      methods: {
        update() {
          this.dataset.source = storage.data().movements || [];
        },
      },
    });
  </script>
</html>
